﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using Common.Utilities;
using Common.Utilities.Dispatcher;

namespace Workflows.Components.TextMining.Clustering
{
    /// <summary>
    /// 
    /// </summary>
    [Serializable]
    public class UnsupervisedClusteringInstruction:InstructionBase 
    {
        #region props
        private string _DocTermListFilePath;
        /// <summary>
        /// <Docs>
        ///     <Doc DocID="0">
        ///         <Term TermID="0" Phrase=""/>
        ///     </Doc>
        /// </Docs>
        /// </summary>
        public string DocTermListFilePath
        {
            get
            {
                return this._DocTermListFilePath;
            }
            set
            {
                this._DocTermListFilePath = value;
            }
        }

        private double _DistanceThreshold;
        /// <summary></summary>
        public double DistanceThreshold
        {
            get
            {
                return this._DistanceThreshold;
            }
            set
            {
                this._DistanceThreshold = value;
            }
        }

        private double _TermCoverageThreshold;
        /// <summary></summary>
        public double TermCoverageThreshold
        {
            get
            {
                return this._TermCoverageThreshold;
            }
            set
            {
                this._TermCoverageThreshold = value;
            }
        }

        private double _TermFreqThreshold;
        /// <summary></summary>
        public double TermFreqThreshold
        {
            get
            {
                return this._TermFreqThreshold;
            }
            set
            {
                this._TermFreqThreshold = value;
            }
        }

        private List<string> _ExcludedTerms;
        /// <summary></summary>
        public List<string> ExcludedTerms
        {
            get
            {
                return this._ExcludedTerms;
            }
            set
            {
                this._ExcludedTerms = value;
            }
        }

        private DistanceCalculationMethod _DistanceMethod;
        /// <summary></summary>
        public DistanceCalculationMethod DistanceMethod
        {
            get
            {
                return this._DistanceMethod;
            }
            set
            {
                this._DistanceMethod = value;
            }
        }

        private DocumentClusteringMethod _ClusterMethod;
        /// <summary></summary>
        public DocumentClusteringMethod ClusterMethod
        {
            get
            {
                return this._ClusterMethod;
            }
            set
            {
                this._ClusterMethod = value;
            }
        }

        private PurityMeasureMethod _PurityMethod;
        /// <summary></summary>
        public PurityMeasureMethod PurityMethod
        {
            get
            {
                return this._PurityMethod;
            }
            set
            {
                this._PurityMethod = value;
            }
        }

        private string _ClusteringResultFilePath;
        /// <summary>
        /// <Clusters>
        ///     <Cluster ClusterID="0" Distance="0.0">
        ///         <Doc DocID="1"/>
        ///     </Cluster>
        /// </Clusters>
        /// </summary>
        public string ClusteringResultFilePath
        {
            get
            {
                return this._ClusteringResultFilePath;
            }
            set
            {
                this._ClusteringResultFilePath = value;
            }
        }

        private bool _CanOverlap;
        /// <summary></summary>
        public bool CanOverlap
        {
            get
            {
                return this._CanOverlap;
            }
            set
            {
                this._CanOverlap = value;
            }
        }

        private double _ReassociationDistanceRange;
        /// <summary>range/distance ratio, default to 1.50</summary>
        public double ReassociationDistanceRange
        {
            get
            {
                return this._ReassociationDistanceRange;
            }
            set
            {
                this._ReassociationDistanceRange = value;
            }
        }

        private TermFuzzyMatchMethod _TermFuzzyMatch;
        /// <summary></summary>
        public TermFuzzyMatchMethod TermFuzzyMatch
        {
            get
            {
                return this._TermFuzzyMatch;
            }
            set
            {
                this._TermFuzzyMatch = value;
            }
        }
        #endregion

        #region ctor
        /// <summary>
        /// 
        /// </summary>
        public UnsupervisedClusteringInstruction():base(Guid.Empty,Guid.Empty)
        {
            this._DocTermListFilePath = string.Empty;
            this._DistanceThreshold = 0.35;
            this._TermCoverageThreshold = 0.25;
            this._TermFreqThreshold = 0.05;
            this._ExcludedTerms=new List<string>();
            this._PurityMethod = PurityMeasureMethod.EntropyMeasure;
            this._DistanceMethod = DistanceCalculationMethod.CosineDistance;
            this._ClusterMethod = DocumentClusteringMethod.KMeans;
            this._ClusteringResultFilePath = string.Empty;
            this._CanOverlap = false;
            this._ReassociationDistanceRange = 1.50;
            this._TermFuzzyMatch = TermFuzzyMatchMethod.None;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="componentID"></param>
        /// <param name="jobTicketID"></param>
        /// <param name="docTermListFilePath"></param>
        /// <param name="distanceThreshold"></param>
        /// <param name="termCoverageThreshold"></param>
        /// <param name="termFreqThreshold"></param>
        /// <param name="excludedTerms"></param>
        /// <param name="purityMethod"></param>
        /// <param name="distanceMethod"></param>
        /// <param name="clusterMethod"></param>
        /// <param name="canOverlap"></param>
        /// <param name="reassociationRange"></param>
        /// <param name="resultFilePath"></param>
        /// <param name="termFuzzyMatchMethod"></param>
        public UnsupervisedClusteringInstruction(Guid componentID, Guid jobTicketID, 
            string docTermListFilePath, 
            double distanceThreshold,
            double termCoverageThreshold,
            double termFreqThreshold,
            List<string> excludedTerms,
            PurityMeasureMethod purityMethod,
            DistanceCalculationMethod distanceMethod,
            DocumentClusteringMethod clusterMethod,
            bool canOverlap, 
            double reassociationRange,
            string resultFilePath,
            TermFuzzyMatchMethod termFuzzyMatchMethod)
            : base(componentID, jobTicketID)
        {
            this._PurityMethod = purityMethod;
            this._DistanceMethod = distanceMethod;
            this._DistanceThreshold = distanceThreshold;
            this._TermCoverageThreshold = termCoverageThreshold;
            this._TermFreqThreshold = termFreqThreshold;
            this._ExcludedTerms = excludedTerms;
            this._ClusterMethod = clusterMethod;
            this._DocTermListFilePath =docTermListFilePath;
            this._ClusteringResultFilePath = resultFilePath;
            this._CanOverlap = canOverlap;
            this._ReassociationDistanceRange = reassociationRange;
            this._TermFuzzyMatch = termFuzzyMatchMethod;
        }

        #endregion

        public override InstructionBase Instantiate(XmlNode xNode)
        {
            if (xNode.Attributes.GetNamedItem("Type").Value == this.GetType().FullName)
            {
                UnsupervisedClusteringInstruction instruction = new UnsupervisedClusteringInstruction();
                instruction.OwnerComponentID = new Guid(xNode.Attributes.GetNamedItem("ComponentID").Value);
                instruction.OwnerWorkerInstanceID = new Guid(xNode.Attributes.GetNamedItem("WorkerID").Value);
                instruction.SubmitTime = DateTime.Parse(xNode.Attributes.GetNamedItem("SubmitTime").Value);
                instruction.StartTime = DateTime.Parse(xNode.Attributes.GetNamedItem("StartTime").Value);

                instruction.DocTermListFilePath = XmlDataUtil.GetAttributeValue(xNode, "DocTermListFilePath", "");
                instruction.TermCoverageThreshold =
                    double.Parse(XmlDataUtil.GetAttributeValue(xNode, "TermCoverageThreshold", "0.25"));
                instruction.DistanceThreshold =
                    double.Parse(XmlDataUtil.GetAttributeValue(xNode, "DistanceThreshold", "0.35"));
                instruction.TermFreqThreshold =
                    double.Parse(XmlDataUtil.GetAttributeValue(xNode, "TermFreqThreshold", "0.05"));
                instruction.PurityMethod =
                    (PurityMeasureMethod)
                    int.Parse(XmlDataUtil.GetAttributeValue(xNode, "PurityMethod",
                                                            ((int) PurityMeasureMethod.EntropyMeasure).ToString()));
                instruction.DistanceMethod =
                    (DistanceCalculationMethod)
                    int.Parse(XmlDataUtil.GetAttributeValue(xNode, "DistanceMethod",
                                                            ((int) DistanceCalculationMethod.CosineDistance).ToString()));
                instruction.ClusterMethod =
                    (DocumentClusteringMethod)
                    int.Parse(XmlDataUtil.GetAttributeValue(xNode, "ClusterMethod",
                                                            ((int) DocumentClusteringMethod.KMeans).ToString()));

                instruction.ClusteringResultFilePath = XmlDataUtil.GetAttributeValue(xNode, "ClusteringResultFilePath",
                                                                                     "");
                instruction.CanOverlap = bool.Parse(XmlDataUtil.GetAttributeValue(xNode, "CanOverlap", false.ToString()));
                instruction.ReassociationDistanceRange =
                    double.Parse(XmlDataUtil.GetAttributeValue(xNode, "ReassociationRange", "1.50"));
                instruction.TermFuzzyMatch =
                    (TermFuzzyMatchMethod) int.Parse(XmlDataUtil.GetAttributeValue(
                                                         xNode, "TermFuzzyMatch",
                                                         ((int) TermFuzzyMatchMethod.None).
                                                             ToString()));

                XmlNodeList excludedTermNodes = xNode.SelectNodes("ExcludedTerms/ExcludedTerm");
                if(excludedTermNodes !=null && excludedTermNodes.Count>0)
                {
                    instruction.ExcludedTerms=new List<string>();
                    foreach(XmlNode excludedTermNode in excludedTermNodes)
                    {
                        instruction.ExcludedTerms.Add(XmlDataUtil.GetAttributeValue(excludedTermNode, "Term", ""));
                    }
                }
                
                return instruction;
            }
            else
            {
                throw new Exception("Unable to instantiate instruction: invalid type specified in xml node");
            }
        }

        public override XmlNode Serialize(ref XmlDocument xDoc, ref XmlNode parentNode)
        {
            XmlNode xNode = XmlDataUtil.AddElement(ref xDoc, parentNode, "Instruction");
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "Type", this.GetType().FullName);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ComponentID", this.OwnerComponentID.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "WorkerID", this.OwnerWorkerInstanceID.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "SubmitTime", this.SubmitTime.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "StartTime", this.StartTime.ToString());

            XmlDataUtil.UpdateAttribute(ref xDoc, xNode,"DocTermListFilePath",this.DocTermListFilePath);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode,"TermCoverageThreshold",this.TermCoverageThreshold.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode,"DistanceThreshold",this.DistanceThreshold.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "TermFreqThreshold", this.TermFreqThreshold.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode,"PurityMethod",((int)this.PurityMethod).ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "DistanceMethod",((int)this.DistanceMethod).ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ClusterMethod", ((int) this.ClusterMethod).ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "CanOverlap", this.CanOverlap.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ClusteringResultFilePath", this.ClusteringResultFilePath);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode,"ReassociationRange",this.ReassociationDistanceRange.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode,"TermFuzzyMatch",((int)this.TermFuzzyMatch).ToString());

            if(this.ExcludedTerms !=null && this.ExcludedTerms.Count>0)
            {
                XmlNode exTermRootNode = XmlDataUtil.AddElement(ref xDoc, xNode, "ExcludedTerms");
                foreach(string exTerm in this.ExcludedTerms)
                {
                    XmlNode exTermNode = XmlDataUtil.AddElement(ref xDoc, exTermRootNode, "ExcludedTerm");
                    XmlDataUtil.UpdateAttribute(ref xDoc, exTermNode, "Term", exTerm);
                }
            }

            return xNode;
        }
    }
}
